package com.zzyl.serve.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zzyl.common.core.domain.entity.SysUser;
import com.zzyl.common.core.domain.model.LoginUser;
import com.zzyl.common.core.page.TableDataInfo;
import com.zzyl.common.exception.base.BaseException;
import com.zzyl.common.utils.SecurityUtils;
import com.zzyl.common.utils.StringUtils;
import com.zzyl.serve.domain.AlertData;
import com.zzyl.serve.dto.AlertDataPageDto;
import com.zzyl.serve.dto.AlertDataProcessDto;
import com.zzyl.serve.mapper.AlertDataMapper;
import com.zzyl.serve.mapper.BedMapper;
import com.zzyl.serve.mapper.ElderMapper;
import com.zzyl.serve.service.IAlertDataService;
import com.zzyl.serve.service.IDeviceService;
import com.zzyl.serve.vo.AlertDataVo;
import com.zzyl.system.mapper.SysRoleMapper;
import com.zzyl.system.service.ISysRoleService;
import com.zzyl.system.service.ISysUserService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 报警数据Service业务层处理
 *
 * @author liuyp
 * @date 2025-08-26
 */
@Service
public class AlertDataServiceImpl extends ServiceImpl<AlertDataMapper, AlertData> implements IAlertDataService {
    @Autowired
    private AlertDataMapper alertDataMapper;
    @Autowired
    private ISysUserService sysUserService;
    @Autowired
    private IDeviceService deviceService;
    @Autowired
    private ElderMapper elderMapper;
    @Autowired
    private ISysRoleService iSysRoleService;
    @Autowired
    private SysRoleMapper sysRoleMapper;
    @Autowired
    private BedMapper bedMapper;

    /**
     * 查询报警数据
     *
     * @param id 报警数据主键
     * @return 报警数据
     */
    @Override
    public AlertData selectAlertDataById(Long id) {
        return alertDataMapper.selectById(id);
    }


    /**
     * 新增报警数据
     *
     * @param alertData 报警数据
     * @return 结果
     */
    @Override
    public int insertAlertData(AlertData alertData) {
        return alertDataMapper.insert(alertData);
    }

    /**
     * 修改报警数据
     *
     * @param alertData 报警数据
     * @return 结果
     */
    @Override
    public int updateAlertData(AlertData alertData) {
        return alertDataMapper.updateById(alertData);
    }

    /**
     * 批量删除报警数据
     *
     * @param ids 需要删除的报警数据主键
     * @return 结果
     */
    @Override
    public int deleteAlertDataByIds(Long[] ids) {
        return alertDataMapper.deleteBatchIds(Arrays.asList(ids));
    }

    /**
     * 删除报警数据信息
     *
     * @param id 报警数据主键
     * @return 结果
     */
    @Override
    public int deleteAlertDataById(Long id) {
        return alertDataMapper.deleteById(id);
    }

    @Override
    public TableDataInfo<AlertDataVo> selectAlertList(AlertDataPageDto dto) {
        /**
         * 查询条件说明：
         * - 当dto.getDeviceName()不为空时，添加设备名称模糊匹配条件
         * - 当dto.getStartTime()和dto.getEndTime()都不为空时，添加处理时间范围查询条件
         * - 当dto.getStatus()不为空时，添加状态精确匹配条件
         */
        LambdaQueryWrapper<AlertData> wrapper = Wrappers.<AlertData>lambdaQuery()
                .like(StringUtils.isNotEmpty(dto.getDeviceName()), AlertData::getDeviceName, dto.getDeviceName())
                .between(dto.getStartTime() != null && dto.getEndTime() != null, AlertData::getCreateTime, dto.getStartTime(), dto.getEndTime())
                .eq(dto.getStatus() != null, AlertData::getStatus, dto.getStatus());

        // 创建分页对象
        Page<AlertData> page = new Page<>(dto.getPageNum(), dto.getPageSize());

        // 执行分页查询
        page = this.page(page, wrapper);

        // 转换为VO对象
        List<AlertDataVo> voList = page.getRecords().stream()
                .map(this::convertToVo)
                .collect(Collectors.toList());

        // 构建返回结果
        TableDataInfo<AlertDataVo> result = new TableDataInfo<>();
        result.setCode(200);
        result.setMsg("查询成功");
        result.setRows(voList);
        result.setTotal(page.getTotal());

        return result;
    }

    @Override
    public void updateHandleAlertData(AlertDataProcessDto dto) {
        //根据id取到当前数据库中alert_data表中的数据,用AlertData实体类封装起来
        AlertData alertData = alertDataMapper.selectById(dto.getId());
        //判断处理状态，如果未处理才进行处理
        if (alertData.getStatus()==0){
            /*
            获取到时间，然后对时间类型转换成LocalDataTime
             */
            Instant isProcessingTime = dto.getProcessingTime();
            LocalDateTime processingTime = isProcessingTime.atZone(ZoneId.systemDefault()).toLocalDateTime();
            //将各种需要的值设置到alert实体类里，例如处理人，处理人名称，处理时间，改变处理状态等等处理
            alertData.setProcessingTime(processingTime);
            alertData.setProcessingResult(dto.getProcessingResult());
            alertData.setStatus(1);
            LoginUser loginUser = SecurityUtils.getLoginUser();
            //查找用户表中，得到用户实体，获取用户的名称;
            SysUser sysUser = sysUserService.selectUserById(loginUser.getUserId());
            alertData.setProcessorName(sysUser.getNickName());
            updateAlertData(alertData);
        }else {
            throw new BaseException("这个报警数据已经被处理");
        }
    }

    /**
     * 将AlertData转换为AlertDataVo
     *
     * @param alertData 报警数据实体
     * @return 报警数据VO
     */
    private AlertDataVo convertToVo(AlertData alertData) {
        //获取登录用户的id
        Long userId = SecurityUtils.getLoginUser().getUserId();
        //判断这个设备是否是绑定给老人的，且这个设备是随身设备则需要找到护理员
        if (alertData.getLocationType() == 0) {
            //获取老人的id
            String elderIdStr = alertData.getAccessLocation();
            Long elderId = Long.valueOf(elderIdStr);
            //根据老人的id查找到护理员的id的集合
            List<Long> nuringIds = elderMapper.selectNursingId(elderId);
            //循环遍历集合
            for (Long nursingId : nuringIds) {
                if (userId.equals(nursingId) || userId == 1) {
                    //调用setDataVo方法获取到需要返回的对象，主要是做了一些数据处理和转换
                    return setDataVo(alertData);
                }
            }
            //如果是固定设备并且不是床位上的
        } else if (alertData.getLocationType() == 1 && alertData.getPhysicalLocationType()!=2) {
            //就需要通知维修工，这属于设备异常，而不是老人数据异常，获取到维修工的id集合
            List<Long> mtUserIds = sysRoleMapper.selectUserIdByRoleName("维修工");
            //遍历集合
            for (Long mtUserId : mtUserIds) {
                if (userId.equals(mtUserId) || userId == 1) {
                    //调用setDataVo方法获取到需要返回的对象，主要是做了一些数据处理和转换
                    return setDataVo(alertData);
                }
            }
            //如果是绑定的床位的设备，还是关乎到老人的数据异常，因此也是通知护理员
        }else if(alertData.getLocationType() == 1 && alertData.getPhysicalLocationType() == 2){
            //得到床位id的字符串
            String bedIdStr = alertData.getAccessLocation();
            //转化为Long类型
            Long bedId = Long.valueOf(bedIdStr);
            //sql语句查询到用户id的集合
            List<Long> userIds = bedMapper.selectUserIdByBedId(bedId);
            //循环遍历id判断该用户是否能查看这些信息
            for (Long nursingId : userIds) {
                if (userId.equals(nursingId) || userId == 1) {
                    //调用setDataVo方法获取到需要返回的对象，主要是做了一些数据处理和转换
                    return setDataVo(alertData);
                }
            }
        }
        return null;
    }

    private AlertDataVo setDataVo(AlertData alertData) {
        AlertDataVo vo = new AlertDataVo();
        //因为两个实体类的属性基本相同，所以调用BeanUtils.copyProperties（）进行复制，只需要处理剩下不同的
        BeanUtils.copyProperties(alertData, vo);
        // AlertData中的deviceName映射到AlertDataVo中的nickName
        vo.setNickname(alertData.getDeviceName());

        // 如果需要从device表获取node_id作为AlertDataVo中的deviceName
        // 假设AlertData中有deviceId字段关联device表
        if (alertData.getIotId() != null) {
            // 这里需要根据实际的device表查询方式来获取node_id
            String nodeId = deviceService.selectByIotid(alertData.getIotId());
            //将nodeId设置vo中
            vo.setDeviceName(nodeId);
        }
        return vo;
    }
}
